﻿/*
* Copyright (c) 2010-2012 Tesla Engine Group
* 
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
* 
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
* 
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/

using System;
using System.Collections.Generic;
using System.IO;
using System.Reflection;
using Tesla.Core;
using Tesla.Graphics;
using Tesla.Math;

namespace Tesla.Content.Loaders {
    /// <summary>
    /// Savable reader that de-serializes an ISavable object from a Tesla Engine Binary Object (TEBO)
    /// file format.
    /// </summary>
    public sealed class BinarySavableReader : ISavableReader{
        private IRenderSystemProvider _renderSystem;
        private BinaryReader _input;
        private ContentManager _content;
        private Dictionary<String, Type> _types;
        private Dictionary<Type, ConstructorInfo> _constructors;
        private bool _isDisposed = false;
        private const sbyte NULL_OBJECT = -1;

        /// <summary>
        /// Gets the render system that will be used to create any graphics objects.
        /// </summary>
        public IRenderSystemProvider RenderSystem {
            get {
                return _renderSystem;
            }
        }

        /// <summary>
        /// Gets the content manager that is used to load any external objects.
        /// </summary>
        public ContentManager ContentManager {
            get {
                return _content;
            }
        }

        /// <summary>
        /// Gets if the reader has been disposed or not.
        /// </summary>
        public bool IsDisposed {
            get {
                return _isDisposed;
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BinarySavableReader"/> class. The default render
        /// system is used from the Engine root.
        /// </summary>
        /// <param name="inputStream">The input stream to read from.</param>
        /// <param name="contentManager">The content manager to load auxillary content.</param>
        public BinarySavableReader(Stream inputStream, ContentManager contentManager) {
            _input = new BinaryReader(inputStream);
            _content = contentManager;
            _renderSystem = Engine.Services.GetService<IRenderSystemProvider>();
            _types = new Dictionary<String, Type>();
            _constructors = new Dictionary<Type, ConstructorInfo>();
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="BinarySavableReader"/> class.
        /// </summary>
        /// <param name="inputStream">The input stream to read from.</param>
        /// <param name="renderSystem">The render system used for creating graphics objects.</param>
        /// <param name="contentManager">The content manager to load auxillary content.</param>
        public BinarySavableReader(Stream inputStream, IRenderSystemProvider renderSystem, ContentManager contentManager) {
            _input = new BinaryReader(inputStream);
            _content = contentManager;
            _renderSystem = renderSystem;
            _types = new Dictionary<String, Type>();
            _constructors = new Dictionary<Type, ConstructorInfo>();
        }

        /// <summary>
        /// Releases unmanaged resources and performs other cleanup operations before the
        /// <see cref="BinarySavableReader"/> is reclaimed by garbage collection.
        /// </summary>
        ~BinarySavableReader() {
            Dispose(false);
        }

        private Object CreateType() {
            String fullyQualifiedName = this.ReadString();
            Type type;
            ConstructorInfo cinfo;
            if(!_types.TryGetValue(fullyQualifiedName, out type)) {
                type = Type.GetType(fullyQualifiedName);
                if(type == null) {
                    throw new ArgumentException("Cannot read savable's type.");
                }
                cinfo = type.GetConstructor(BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance, null, new Type[0], null);
                _types.Add(fullyQualifiedName, type);
                _constructors.Add(type, cinfo);
            } else {
                cinfo = _constructors[type];
            }


            return cinfo.Invoke(null);
        }

        private Type ReadType() {
            String fullyQualifiedName = this.ReadString();
            Type type;
            if(!_types.TryGetValue(fullyQualifiedName, out type)) {
                type = Type.GetType(fullyQualifiedName);
                if(type == null) {
                    throw new ArgumentException("cannot read savable's type.");
                }
                _types.Add(fullyQualifiedName, type);
            }
            return type;
        }

        /// <summary>
        /// Reads a single byte from the input.
        /// </summary>
        /// <returns>
        /// Read byte
        /// </returns>
        public byte ReadByte() {
            return _input.ReadByte();
        }

        /// <summary>
        /// Reads an array of bytes from the input.
        /// </summary>
        /// <returns>
        /// Array of bytes
        /// </returns>
        public byte[] ReadByteArray() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int num = _input.ReadInt32();
            return _input.ReadBytes(num);
        }

        /// <summary>
        /// Reads a 2D array of bytes from the input.
        /// </summary>
        /// <returns>
        /// 2D array of bytes
        /// </returns>
        public byte[,] ReadByteArray2D() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int row = _input.ReadInt32();
            int col = _input.ReadInt32();
            byte[,] arr = new byte[row, col];
            for(int i = 0; i < row; i++) {
                for(int j = 0; j < row; j++) {
                    arr[i, j] = _input.ReadByte();
                }
            }
            return arr;
        }

        /// <summary>
        /// Reads a single sbyte from the input.
        /// </summary>
        /// <returns>
        /// Read sbyte
        /// </returns>
        public sbyte ReadSByte() {
            return _input.ReadSByte();
        }

        /// <summary>
        /// Reads an array of sbytes from the input.
        /// </summary>
        /// <returns>
        /// Array of sbytes
        /// </returns>
        public sbyte[] ReadSByteArray() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int num = _input.ReadInt32();
            sbyte[] bytes = new sbyte[num];
            for(int i = 0; i < num; i++) {
                bytes[i] = _input.ReadSByte();
            }
            return bytes;
        }

        /// <summary>
        /// Reads a 2D array of sbytes from the input.
        /// </summary>
        /// <returns>
        /// 2D array of sbytes
        /// </returns>
        public sbyte[,] ReadSByteArray2D() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int row = _input.ReadInt32();
            int col = _input.ReadInt32();
            sbyte[,] arr = new sbyte[row, col];
            for(int i = 0; i < row; i++) {
                for(int j = 0; j < row; j++) {
                    arr[i, j] = _input.ReadSByte();
                }
            }
            return arr;
        }

        /// <summary>
        /// Reads a single char from the input.
        /// </summary>
        /// <returns>
        /// Read char
        /// </returns>
        public char ReadChar() {
            return _input.ReadChar();
        }

        /// <summary>
        /// Reads an array of char from the input.
        /// </summary>
        /// <returns>
        /// Array of char
        /// </returns>
        public char[] ReadCharArray() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int num = _input.ReadInt32();
            return _input.ReadChars(num);
        }

        /// <summary>
        /// Reads a 2D array of char from the input.
        /// </summary>
        /// <returns>
        /// 2D array of char
        /// </returns>
        public char[,] ReadCharArray2D() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int row = _input.ReadInt32();
            int col = _input.ReadInt32();
            char[,] arr = new char[row, col];
            for(int i = 0; i < row; i++) {
                for(int j = 0; j < row; j++) {
                    arr[i, j] = _input.ReadChar();
                }
            }
            return arr;
        }

        /// <summary>
        /// Reads a single unsigned 16-bit int from the input.
        /// </summary>
        /// <returns>
        /// Read UInt16
        /// </returns>
        public ushort ReadUInt16() {
            return _input.ReadUInt16();
        }

        /// <summary>
        /// Reads an array of unsigned 16-bit ints from the input.
        /// </summary>
        /// <returns>
        /// Array of UInt16
        /// </returns>
        public ushort[] ReadUInt16Array() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int num = _input.ReadInt32();
            ushort[] arr = new ushort[num];
            for(int i = 0; i < num; i++) {
                arr[i] = _input.ReadUInt16();
            }
            return arr;
        }

        /// <summary>
        /// Reads a 2D array of unsigned 16-bit ints from the input.
        /// </summary>
        /// <returns>
        /// 2D array of UInt16
        /// </returns>
        public ushort[,] ReadUInt16Array2D() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int row = _input.ReadInt32();
            int col = _input.ReadInt32();
            ushort[,] arr = new ushort[row, col];
            for(int i = 0; i < row; i++) {
                for(int j = 0; j < row; j++) {
                    arr[i, j] = _input.ReadUInt16();
                }
            }
            return arr;
        }

        /// <summary>
        /// Reads a single unsigned 32-bit int from the input.
        /// </summary>
        /// <returns>
        /// Read UInt32
        /// </returns>
        public uint ReadUInt32() {
            return _input.ReadUInt32();
        }

        /// <summary>
        /// Reads an array of unsigned 32-bits int from the input.
        /// </summary>
        /// <returns>
        /// Array of UInt32
        /// </returns>
        public uint[] ReadUInt32Array() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int num = _input.ReadInt32();
            uint[] arr = new uint[num];
            for(int i = 0; i < num; i++) {
                arr[i] = _input.ReadUInt32();
            }
            return arr;
        }

        /// <summary>
        /// Reads a 2D array of unsigned 32-bit ints from the input.
        /// </summary>
        /// <returns>
        /// 2D array of UInt32
        /// </returns>
        public uint[,] ReadUInt32Array2D() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int row = _input.ReadInt32();
            int col = _input.ReadInt32();
            uint[,] arr = new uint[row, col];
            for(int i = 0; i < row; i++) {
                for(int j = 0; j < row; j++) {
                    arr[i, j] = _input.ReadUInt32();
                }
            }
            return arr;
        }

        /// <summary>
        /// Reads a single unsigned 64-bit int from the input.
        /// </summary>
        /// <returns>
        /// Read UInt64
        /// </returns>
        public ulong ReadUInt64() {
            return _input.ReadUInt64();
        }

        /// <summary>
        /// Reads an array of unsigned 64-bits int from the input.
        /// </summary>
        /// <returns>
        /// Array of UInt64
        /// </returns>
        public ulong[] ReadUInt64Array() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int num = _input.ReadInt32();
            ulong[] arr = new ulong[num];
            for(int i = 0; i < num; i++) {
                arr[i] = _input.ReadUInt64();
            }
            return arr;
        }

        /// <summary>
        /// Reads a 2D array of unsigned 64-bit ints from the input.
        /// </summary>
        /// <returns>
        /// 2D array of UInt64
        /// </returns>
        public ulong[,] ReadUInt64Array2D() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int row = _input.ReadInt32();
            int col = _input.ReadInt32();
            ulong[,] arr = new ulong[row, col];
            for(int i = 0; i < row; i++) {
                for(int j = 0; j < row; j++) {
                    arr[i, j] = _input.ReadUInt64();
                }
            }
            return arr;
        }

        /// <summary>
        /// Reads a single 16-bit int from the input.
        /// </summary>
        /// <returns>
        /// Read Int16
        /// </returns>
        public short ReadShort() {
            return _input.ReadInt16();
        }

        /// <summary>
        /// Reads an array of 16-bits int from the input.
        /// </summary>
        /// <returns>
        /// Array of Int16
        /// </returns>
        public short[] ReadShortArray() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int num = _input.ReadInt32();
            short[] arr = new short[num];
            for(int i = 0; i < num; i++) {
                arr[i] = _input.ReadInt16();
            }
            return arr;
        }

        /// <summary>
        /// Reads a 2D array of 16-bit ints from the input.
        /// </summary>
        /// <returns>
        /// 2D array of Int16
        /// </returns>
        public short[,] ReadShortArray2D() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int row = _input.ReadInt32();
            int col = _input.ReadInt32();
            short[,] arr = new short[row, col];
            for(int i = 0; i < row; i++) {
                for(int j = 0; j < row; j++) {
                    arr[i, j] = _input.ReadInt16();
                }
            }
            return arr;
        }

        /// <summary>
        /// Reads a single 32-bit int from the input.
        /// </summary>
        /// <returns>
        /// Read Int32
        /// </returns>
        public int ReadInt() {
            return _input.ReadInt32();
        }

        /// <summary>
        /// Reads an array of 32-bits int from the input.
        /// </summary>
        /// <returns>
        /// Array of Int32
        /// </returns>
        public int[] ReadIntArray() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int num = _input.ReadInt32();
            int[] arr = new int[num];
            for(int i = 0; i < num; i++) {
                arr[i] = _input.ReadInt32();
            }
            return arr;
        }

        /// <summary>
        /// Reads a 2D array of 32-bit ints from the input.
        /// </summary>
        /// <returns>
        /// 2D array of Int32
        /// </returns>
        public int[,] ReadIntArray2D() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int row = _input.ReadInt32();
            int col = _input.ReadInt32();
            int[,] arr = new int[row, col];
            for(int i = 0; i < row; i++) {
                for(int j = 0; j < row; j++) {
                    arr[i, j] = _input.ReadInt32();
                }
            }
            return arr;
        }

        /// <summary>
        /// Reads a single 64-bit int from the input.
        /// </summary>
        /// <returns>
        /// Read Int64
        /// </returns>
        public long ReadLong() {
            return _input.ReadInt64();
        }

        /// <summary>
        /// Reads an array of 64-bits int from the input.
        /// </summary>
        /// <returns>
        /// Array of Int64
        /// </returns>
        public long[] ReadLongArray() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int num = _input.ReadInt32();
            long[] arr = new long[num];
            for(int i = 0; i < num; i++) {
                arr[i] = _input.ReadInt64();
            }
            return arr;
        }

        /// <summary>
        /// Reads a 2D array of 64-bit ints from the input.
        /// </summary>
        /// <returns>
        /// 2D array of Int64
        /// </returns>
        public long[,] ReadLongArray2D() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int row = _input.ReadInt32();
            int col = _input.ReadInt32();
            long[,] arr = new long[row, col];
            for(int i = 0; i < row; i++) {
                for(int j = 0; j < row; j++) {
                    arr[i, j] = _input.ReadInt64();
                }
            }
            return arr;
        }

        /// <summary>
        /// Reads a single float from the input.
        /// </summary>
        /// <returns>
        /// Read float
        /// </returns>
        public float ReadSingle() {
            return _input.ReadSingle();
        }

        /// <summary>
        /// Reads an array of floats from the input.
        /// </summary>
        /// <returns>
        /// Array of floats
        /// </returns>
        public float[] ReadSingleArray() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int num = _input.ReadInt32();
            float[] arr = new float[num];
            for(int i = 0; i < num; i++) {
                arr[i] = _input.ReadSingle();
            }
            return arr;
        }

        /// <summary>
        /// Reads a 2D array of floats from the input.
        /// </summary>
        /// <returns>
        /// 2D array of floats
        /// </returns>
        public float[,] ReadSingleArray2D() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int row = _input.ReadInt32();
            int col = _input.ReadInt32();
            float[,] arr = new float[row, col];
            for(int i = 0; i < row; i++) {
                for(int j = 0; j < row; j++) {
                    arr[i, j] = _input.ReadSingle();
                }
            }
            return arr;
        }

        /// <summary>
        /// Reads a single double from the input.
        /// </summary>
        /// <returns>
        /// Read double
        /// </returns>
        public double ReadDouble() {
            return _input.ReadDouble();
        }

        /// <summary>
        /// Reads an array of doubles from the input.
        /// </summary>
        /// <returns>
        /// Array of doubles
        /// </returns>
        public double[] ReadDoubleArray() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int num = _input.ReadInt32();
            double[] arr = new double[num];
            for(int i = 0; i < num; i++) {
                arr[i] = _input.ReadDouble();
            }
            return arr;
        }

        /// <summary>
        /// Reads a 2D array of doubles from the input.
        /// </summary>
        /// <returns>
        /// 2D array of doubles
        /// </returns>
        public double[,] ReadDoubleArray2D() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int row = _input.ReadInt32();
            int col = _input.ReadInt32();
            double[,] arr = new double[row, col];
            for(int i = 0; i < row; i++) {
                for(int j = 0; j < row; j++) {
                    arr[i, j] = _input.ReadDouble();
                }
            }
            return arr;
        }

        /// <summary>
        /// Reads a single bool from the input.
        /// </summary>
        /// <returns>
        /// Read bool
        /// </returns>
        public bool ReadBoolean() {
            return _input.ReadBoolean();
        }

        /// <summary>
        /// Reads an array of bools from the input.
        /// </summary>
        /// <returns>
        /// Array of bools
        /// </returns>
        public bool[] ReadBooleanArray() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int num = _input.ReadInt32();
            bool[] arr = new bool[num];
            for(int i = 0; i < num; i++) {
                arr[i] = _input.ReadBoolean();
            }
            return arr;
        }

        /// <summary>
        /// Reads a 2D array of bools from the input.
        /// </summary>
        /// <returns>
        /// 2D array of bools
        /// </returns>
        public bool[,] ReadBooleanArray2D() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int row = _input.ReadInt32();
            int col = _input.ReadInt32();
            bool[,] arr = new bool[row, col];
            for(int i = 0; i < row; i++) {
                for(int j = 0; j < row; j++) {
                    arr[i, j] = _input.ReadBoolean();
                }
            }
            return arr;
        }

        /// <summary>
        /// Reads a string from the input.
        /// </summary>
        /// <returns>
        /// Read string
        /// </returns>
        public String ReadString() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            return _input.ReadString();
        }

        /// <summary>
        /// Reads an array of strings from the input.
        /// </summary>
        /// <returns>
        /// Array of strings
        /// </returns>
        public String[] ReadStringArray() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int num = _input.ReadInt32();
            String[] arr = new String[num];
            for(int i = 0; i < num; i++) {
                arr[i] = _input.ReadString();
            }
            return arr;
        }

        /// <summary>
        /// Reads a 2D array of strings from the input.
        /// </summary>
        /// <returns>
        /// 2D array of strings
        /// </returns>
        public String[,] ReadStringArray2D() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int row = _input.ReadInt32();
            int col = _input.ReadInt32();
            String[,] arr = new String[row, col];
            for(int i = 0; i < row; i++) {
                for(int j = 0; j < row; j++) {
                    arr[i, j] = _input.ReadString();
                }
            }
            return arr;
        }

        /// <summary>
        /// Reads a single color struct from the input.
        /// </summary>
        /// <returns>
        /// Color struct
        /// </returns>
        public Color ReadColor() {
            return new Color(_input.ReadUInt32());
        }

        /// <summary>
        /// Reads an array of color structs from the input.
        /// </summary>
        /// <returns>
        /// Array of color structs
        /// </returns>
        public Color[] ReadColorArray() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int num = _input.ReadInt32();
            Color[] arr = new Color[num];
            for(int i = 0; i < num; i++) {
                arr[i] = new Color(_input.ReadUInt32());
            }
            return arr;
        }

        /// <summary>
        /// Reads a 2D array of color structs from the input.
        /// </summary>
        /// <returns>
        /// 2D array of color structs
        /// </returns>
        public Color[,] ReadColorArray2D() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int row = _input.ReadInt32();
            int col = _input.ReadInt32();
            Color[,] arr = new Color[row, col];
            for(int i = 0; i < row; i++) {
                for(int j = 0; j < row; j++) {
                    arr[i, j] = new Color(_input.ReadUInt32());
                }
            }
            return arr;
        }

        /// <summary>
        /// Reads a single matrix struct from the input.
        /// </summary>
        /// <returns>
        /// Matrix struct
        /// </returns>
        public Matrix ReadMatrix() {
            Matrix m;
            m.M11 = _input.ReadSingle();
            m.M12 = _input.ReadSingle();
            m.M13 = _input.ReadSingle();
            m.M14 = _input.ReadSingle();

            m.M21 = _input.ReadSingle();
            m.M22 = _input.ReadSingle();
            m.M23 = _input.ReadSingle();
            m.M24 = _input.ReadSingle();

            m.M31 = _input.ReadSingle();
            m.M32 = _input.ReadSingle();
            m.M33 = _input.ReadSingle();
            m.M34 = _input.ReadSingle();

            m.M41 = _input.ReadSingle();
            m.M42 = _input.ReadSingle();
            m.M43 = _input.ReadSingle();
            m.M44 = _input.ReadSingle();

            return m;
        }

        /// <summary>
        /// Reads an array of matrix structs from the input.
        /// </summary>
        /// <returns>
        /// Array of matrix structs
        /// </returns>
        public Matrix[] ReadMatrixArray() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int num = _input.ReadInt32();
            Matrix[] arr = new Matrix[num];
            for(int i = 0; i < num; i++) {
                arr[i] = ReadMatrix();
            }
            return arr;
        }

        /// <summary>
        /// Reads a 2D array of matrix structs from the input.
        /// </summary>
        /// <returns>
        /// 2D array of matrix structs
        /// </returns>
        public Matrix[,] ReadMatrixArray2D() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int row = _input.ReadInt32();
            int col = _input.ReadInt32();
            Matrix[,] arr = new Matrix[row, col];
            for(int i = 0; i < row; i++) {
                for(int j = 0; j < row; j++) {
                    arr[i, j] = ReadMatrix();
                }
            }
            return arr;
        }

        /// <summary>
        /// Reads a single quaternion struct from the input.
        /// </summary>
        /// <returns>
        /// Quaternion struct
        /// </returns>
        public Quaternion ReadQuaternion() {
            return new Quaternion(_input.ReadSingle(), _input.ReadSingle(), _input.ReadSingle(), _input.ReadSingle());
        }

        /// <summary>
        /// Reads an array of quaternion structs from the input.
        /// </summary>
        /// <returns>
        /// Array of quaternion structs
        /// </returns>
        public Quaternion[] ReadQuaternionArray() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int num = _input.ReadInt32();
            Quaternion[] arr = new Quaternion[num];
            for(int i = 0; i < num; i++) {
                arr[i] = ReadQuaternion();
            }
            return arr;
        }

        /// <summary>
        /// Reads a 2D array of quaternion structs from the input.
        /// </summary>
        /// <returns>
        /// 2D array of quaternion structs
        /// </returns>
        public Quaternion[,] ReadQuaternionArray2D() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int row = _input.ReadInt32();
            int col = _input.ReadInt32();
            Quaternion[,] arr = new Quaternion[row, col];
            for(int i = 0; i < row; i++) {
                for(int j = 0; j < row; j++) {
                    arr[i, j] = ReadQuaternion();
                }
            }
            return arr;
        }

        /// <summary>
        /// Reads a single Vector2 struct from the input.
        /// </summary>
        /// <returns>
        /// Vector2 struct
        /// </returns>
        public Vector2 ReadVector2() {
            return new Vector2(_input.ReadSingle(), _input.ReadSingle());
        }

        /// <summary>
        /// Reads an array of Vector2 structs from the input.
        /// </summary>
        /// <returns>
        /// Array of Vector2 structs
        /// </returns>
        public Vector2[] ReadVector2Array() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int num = _input.ReadInt32();
            Vector2[] arr = new Vector2[num];
            for(int i = 0; i < num; i++) {
                arr[i] = ReadVector2();
            }
            return arr;
        }

        /// <summary>
        /// Reads a 2D array of Vector2 structs from the input.
        /// </summary>
        /// <returns>
        /// 2D array of Vector2 structs
        /// </returns>
        public Vector2[,] ReadVector2Array2D() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int row = _input.ReadInt32();
            int col = _input.ReadInt32();
            Vector2[,] arr = new Vector2[row, col];
            for(int i = 0; i < row; i++) {
                for(int j = 0; j < row; j++) {
                    arr[i, j] = ReadVector2();
                }
            }
            return arr;
        }

        /// <summary>
        /// Reads a single Vector3 struct from the input.
        /// </summary>
        /// <returns>
        /// Vector3 struct
        /// </returns>
        public Vector3 ReadVector3() {
            return new Vector3(_input.ReadSingle(), _input.ReadSingle(), _input.ReadSingle());
        }

        /// <summary>
        /// Reads an array of Vector3 structs from the input.
        /// </summary>
        /// <returns>
        /// Array of Vector3 structs
        /// </returns>
        public Vector3[] ReadVector3Array() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int num = _input.ReadInt32();
            Vector3[] arr = new Vector3[num];
            for(int i = 0; i < num; i++) {
                arr[i] = ReadVector3();
            }
            return arr;
        }

        /// <summary>
        /// Reads a 2D array of Vector3 structs from the input.
        /// </summary>
        /// <returns>
        /// 2D array of Vector3 structs
        /// </returns>
        public Vector3[,] ReadVector3Array2D() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int row = _input.ReadInt32();
            int col = _input.ReadInt32();
            Vector3[,] arr = new Vector3[row, col];
            for(int i = 0; i < row; i++) {
                for(int j = 0; j < row; j++) {
                    arr[i, j] = ReadVector3();
                }
            }
            return arr;
        }

        /// <summary>
        /// Reads a single Vector4 struct from the input.
        /// </summary>
        /// <returns>
        /// Vector4 struct
        /// </returns>
        public Vector4 ReadVector4() {
            return new Vector4(_input.ReadSingle(), _input.ReadSingle(), _input.ReadSingle(), _input.ReadSingle());
        }

        /// <summary>
        /// Reads an array of Vector4 structs from the input.
        /// </summary>
        /// <returns>
        /// Array of Vector4 structs
        /// </returns>
        public Vector4[] ReadVector4Array() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int num = _input.ReadInt32();
            Vector4[] arr = new Vector4[num];
            for(int i = 0; i < num; i++) {
                arr[i] = ReadVector4();
            }
            return arr;
        }

        /// <summary>
        /// Reads a 2D array of Vector4 structs from the input.
        /// </summary>
        /// <returns>
        /// 2D array of Vector4 structs
        /// </returns>
        public Vector4[,] ReadVector4Array2D() {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int row = _input.ReadInt32();
            int col = _input.ReadInt32();
            Vector4[,] arr = new Vector4[row, col];
            for(int i = 0; i < row; i++) {
                for(int j = 0; j < row; j++) {
                    arr[i, j] = ReadVector4();
                }
            }
            return arr;
        }

        /// <summary>
        /// Reads an enum value from the input.
        /// </summary>
        /// <typeparam name="T">Enum type</typeparam>
        /// <returns>
        /// Enum value
        /// </returns>
        public T ReadEnum<T>() where T : struct {
            String name = this.ReadString();
            return (T) Enum.Parse(typeof(T), name, false);
        }

        /// <summary>
        /// Reads a savable object from the input.
        /// </summary>
        /// <typeparam name="T">Type of object to read</typeparam>
        /// <returns>
        /// Object read
        /// </returns>
        public T ReadSavable<T>() where T : ISavable {
            if(_input.ReadSByte() == -1) {
                return default(T);
            }

            Object obj = CreateType();
            if(!(obj is T)) {
                throw new InvalidCastException("Cannot read savable as the requested type.");
            }

            T savable = (T) obj;
            savable.Read(this);
            return savable;
        }

        /// <summary>
        /// Reads an array of savable objects from the input.
        /// </summary>
        /// <typeparam name="T">Type of object to read</typeparam>
        /// <returns>
        /// Array of objects read
        /// </returns>
        public T[] ReadSavableArray<T>() where T : ISavable {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int num = _input.ReadInt32();
            T[] arr = new T[num];
            for(int i = 0; i < num; i++) {
                arr[i] = ReadSavable<T>();
            }
            return arr;
        }

        /// <summary>
        /// Reads a 2D array of savable objects from the input.
        /// </summary>
        /// <typeparam name="T">Type of object to read</typeparam>
        /// <returns>
        /// 2D array of objects read
        /// </returns>
        public T[,] ReadSavableArray2D<T>() where T : ISavable {
            if(_input.ReadSByte() == -1) {
                return null;
            }
            int row = _input.ReadInt32();
            int col = _input.ReadInt32();
            T[,] arr = new T[row, col];
            for(int i = 0; i < row; i++) {
                for(int j = 0; j < row; j++) {
                    arr[i, j] = ReadSavable<T>();
                }
            }
            return arr;
        }

        /// <summary>
        /// Reads a reference to another input that is external to this input.
        /// </summary>
        /// <typeparam name="T">Type of object to read</typeparam>
        /// <returns>
        /// Object read
        /// </returns>
        public T ReadExternal<T>() where T : ISavable {
            if(_input.ReadSByte() == -1) {
                return default(T);
            }

            bool isExternal = _input.ReadBoolean();
            if(isExternal) {
                ExternalReference extReference = ReadSavable<ExternalReference>();
                return ContentManager.Load<T>(extReference.FileName, extReference.LoaderParameters);
            }

            //Otherwise load the savable as usual
            Object obj = CreateType();
            if(!(obj is T)) {
                throw new InvalidCastException("Cannot read savable as the requested type.");
            }

            T savable = (T) obj;
            savable.Read(this);
            return savable;
        }

        /// <summary>
        /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources.
        /// </summary>
        public void Dispose() {
            Dispose(true);
            GC.SuppressFinalize(this);
        }

        private void Dispose(bool disposing) {
            if(!_isDisposed) {
                if(disposing) {
                    _input.Dispose();
                }
            }
            _isDisposed = true;
        }
    }
}
